﻿using System;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Castle.Facilities.Logging;
using Abp.AspNetCore;
using Abp.AspNetCore.Mvc.Antiforgery;
using Abp.Castle.Logging.Log4Net;
using Abp.Extensions;
using SportsPlatform.Configuration;
using SportsPlatform.Identity;
using Abp.AspNetCore.SignalR.Hubs;
using Abp.Dependency;
using Abp.Json;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json.Serialization;
using System.IO;
using Microsoft.Extensions.FileProviders;
using Senparc.Weixin.RegisterServices;
using Senparc.Weixin.AspNet;
using Senparc.Weixin.MP;
using Senparc.Weixin.TenPayV3;
using SportsPlatform.Filters;
using SportsPlatform.Web.Host.FrameworkClass;
using Senparc.CO2NET;
using SportsPlatform.Global;
using Microsoft.Extensions.Primitives;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using SportsPlatform.Redis;
using SportsPlatform.Repository.ScheduleJobRepository;
using SportsPlatform.SportBusiness.ISportBusiness;
using SportsPlatform.SportBusiness.SportBusiness;
//using Autofac;
using SportsPlatform.Web.Host.IoCDependencyInjection;
using Swashbuckle.AspNetCore.Filters;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using System.Threading.Tasks;
using System.Security.Permissions;
using SportsPlatform.Repository.CompetitionRepository;
using SportsPlatform.Application.SportBusiness.SportBusiness;
using SportsPlatform.Application.SportBusiness.ISportBusiness;
using SportsPlatform.Application.Repository.EmployeeRepository;

namespace SportsPlatform.Web.Host.Startup
{
    public class Startup
    {
        private const string _defaultCorsPolicyName = "localhost";

        private const string _apiVersion = "v1";

        private readonly IConfigurationRoot _appConfiguration;
        private readonly IWebHostEnvironment _hostingEnvironment;

        public IConfiguration Configuration { get; }

        public Startup(IWebHostEnvironment env, IConfiguration configuration)
        {
            _hostingEnvironment = env;
            _appConfiguration = env.GetAppConfiguration();
            Configuration = configuration;
        }
        //public void ConfigureContainer(ContainerBuilder builder)
        //{
        //    #region 注册IoC控制反转

        //    builder.RegisterModule<DependencyAutoInjection>();

        //    #endregion
        //}
        public void ConfigureServices(IServiceCollection services)
        {
            #region 初始化系统全局配置

            InitConfiguration();

            //全局静态配置热更新
            ChangeToken.OnChange(() => Configuration.GetReloadToken(), () =>
            {
                InitConfiguration();
            });

            #endregion

            #region 注册系统全局过滤器

            services.AddMvc(options =>
            {
                options.Filters.Add<ActionAuthorization>(); // 添加身份验证过滤器
            });

            services.AddSingleton<LogAttribute>();// 添加审计日志过滤器

            #endregion

            //services.AddMvc(options =>
            //{
            //    options.Filters.Add(typeof(AuthonizationFilter));
            //});

            //MVC
            services.AddControllersWithViews(
                options => { options.Filters.Add(new AbpAutoValidateAntiforgeryTokenAttribute()); }
            ).AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.ContractResolver = new AbpMvcContractResolver(IocManager.Instance)
                {
                    NamingStrategy = new CamelCaseNamingStrategy()
                };
            });
            //实体转换按照实体字段，不会变成小写
            services.AddControllers().AddJsonOptions(config =>
            {
                config.JsonSerializerOptions.PropertyNamingPolicy = null;
            });

            #region 注册AspNetCoreRateLimit组件

            services.AddDistributedRedisCache(options =>
            {
                options.Configuration = UldAdminGlobalContext.RedisConfig.ConnectionString;
                options.InstanceName = "UldAdminRateLimit";
            });

            //加载配置
            services.AddOptions();

            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddSingleton<IRedisRepository, RedisRepository>();
            services.AddSingleton<IScheduleJobService, ScheduleJobService>();
            services.AddSingleton<IScheduleJobRepository, ScheduleJobRepository>();
            services.AddSingleton<IEmployeeRepository, EmployeeRepository>();
            services.AddSingleton<ICompetitionRepository, CompetitionRepository>();
            services.AddSingleton<IHomService, HomeService>();


            #endregion

            #region 添加微信配置

            //使用本地缓存必须添加
            services.AddMemoryCache();

            //Senparc.Weixin 注册（必须）
            services.AddSenparcWeixinServices(_appConfiguration);

            #endregion

            IdentityRegistrar.Register(services);
            AuthConfigurer.Configure(services, _appConfiguration);

            services.AddSignalR();

            // Configure CORS for angular2 UI
            services.AddCors(
                options => options.AddPolicy(
                    _defaultCorsPolicyName,
                    builder => builder
                        .WithOrigins(
                            // App:CorsOrigins in appsettings.json can contain more than one address separated by comma.
                            _appConfiguration["App:CorsOrigins"]
                                .Split(",", StringSplitOptions.RemoveEmptyEntries)
                                .Select(o => o.RemovePostFix("/"))
                                .ToArray()
                        )
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowCredentials()
                )
            );

            // Swagger - Enable this line and the related lines in Configure method to enable swagger UI
            ConfigureSwagger(services);

            // Configure Abp and Dependency Injection
            services.AddAbpWithoutCreatingServiceProvider<SportsPlatformWebHostModule>(
                // Configure Log4Net logging
                options => options.IocManager.IocContainer.AddFacility<LoggingFacility>(
                    f => f.UseAbpLog4Net().WithConfig(_hostingEnvironment.IsDevelopment()
                        ? "log4net.config"
                        : "log4net.Production.config"
                    )
                )
            );

        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory, IHostApplicationLifetime aft)
        {
            #region 启用系统Jwt认证中间件

            if (UldAdminGlobalContext.JwtSettings.IsEnabled)
            {
                //认证中间件
                app.UseAuthentication();
            }

            #endregion

            #region 启用系统全局启停事件

            aft.ApplicationStarted.Register(async () =>
            {
                Console.WriteLine("开始接受报名");

                //加载系统级别缓存
                await UldAdminApplication.LoadSystemCache(app);

                //启动业务作业
                await UldAdminApplication.StartScheduleJobAsync(app);

            });

            aft.ApplicationStopped.Register(() =>
            {
                Console.WriteLine("应用程序已停止");
            });

            aft.ApplicationStopping.Register(async () =>
            {
                Console.WriteLine("应用程序正在停止");

                //清理系统缓存
                await UldAdminApplication.ClearSystemCache(app);

            });

            #endregion
            //启用全局统一异常处理
            app.UseMiddleware<GlobalExceptionHandlerExtensions>();
            #region 启用系统Jwt认证中间件

            if (UldAdminGlobalContext.JwtSettings.IsEnabled)
            {
                //认证中间件
                app.UseAuthentication();
            }

            #endregion
            app.UseAbp(options => { options.UseAbpRequestLocalization = true; }); // Initializes ABP framework.

            app.UseCors(c =>
            {
                c.AllowAnyOrigin();
                c.AllowAnyMethod();
                c.AllowAnyHeader();
            }); // Enable CORS!


            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAbpRequestLocalization();

            #region 启用微信配置

            //启用微信配置（必须）
            var registerService = app.UseSenparcWeixin(env,
                null /* 不为 null 则覆盖 appsettings  中的 SenpacSetting 配置*/,
                null /* 不为 null 则覆盖 appsettings  中的 SenpacWeixinSetting 配置*/,
                register => { /* CO2NET 全局配置 */ },
                (register, weixinSetting) =>
                {
                    register.RegisterMpAccount(weixinSetting, "优联达小程序");
                    //注册微信支付（可以执行多次，注册多个微信支付）
                    register.RegisterTenpayApiV3(weixinSetting, "优联达微信支付");
                });
            #endregion

            //Configure方法添加上认证、授权两个中间件
            app.UseAuthentication();    //认证
            app.UseAuthorization();     //授权

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<AbpCommonHub>("/signalr");
                endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
                endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}");
            });

            app.UseStaticFiles();
            #region 启用系统Swagger组件

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint($"/swagger/{_apiVersion}/swagger.json", $"SportsPlatform API {_apiVersion}"));
            }
            else if (env.IsProduction() && UldAdminGlobalContext.SystemConfig.SupportSwaggerOnProduction)
            {
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint($"/swagger/{_apiVersion}/swagger.json", $"SportsPlatform API {_apiVersion}"));
            }

            #endregion

        }

        private void ConfigureSwagger(IServiceCollection services)
        {
            services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc(_apiVersion, new OpenApiInfo
                {
                    Version = _apiVersion,
                    Title = "SportsPlatform API",
                    Description = "SportsPlatform",
                    // uncomment if needed TermsOfService = new Uri("https://example.com/terms"),
                    Contact = new OpenApiContact
                    {
                        Name = "SportsPlatform",
                        Email = string.Empty,
                        Url = new Uri("https://twitter.com/aspboilerplate"),
                    },
                    License = new OpenApiLicense
                    {
                        Name = "MIT License",
                        Url = new Uri("https://github.com/aspnetboilerplate/aspnetboilerplate/blob/dev/LICENSE"),
                    }
                });
                options.DocInclusionPredicate((docName, description) => true);

                if (UldAdminGlobalContext.JwtSettings.IsEnabled)
                {
                    // header添加token
                    options.OperationFilter<SecurityRequirementsOperationFilter>();

                    options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
                    {
                        Description = "JWT授权，在下框中输入Bearer token",//注意两者之间是一个空格
                        Name = "Authorization",//jwt默认的参数名称
                        In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                        Type = SecuritySchemeType.ApiKey
                    });
                }

                //add summaries to swagger
                bool canShowSummaries = _appConfiguration.GetValue<bool>("Swagger:ShowSummaries");
                if (canShowSummaries)
                {
                    var hostXmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                    var hostXmlPath = Path.Combine(AppContext.BaseDirectory, hostXmlFile);
                    options.IncludeXmlComments(hostXmlPath);

                    var applicationXml = $"SportsPlatform.Application.xml";
                    var applicationXmlPath = Path.Combine(AppContext.BaseDirectory, applicationXml);
                    options.IncludeXmlComments(applicationXmlPath);

                    var webCoreXmlFile = $"SportsPlatform.Web.Core.xml";
                    var webCoreXmlPath = Path.Combine(AppContext.BaseDirectory, webCoreXmlFile);
                    options.IncludeXmlComments(webCoreXmlPath);
                }
            });

            #region 注册系统全局Jwt认证

            if (UldAdminGlobalContext.JwtSettings.IsEnabled)
            {
                JwtSettings jwtSettings = new JwtSettings();
                services.Configure<JwtSettings>(Configuration.GetSection("JwtSettings"));
                Configuration.GetSection("JwtSettings").Bind(jwtSettings);

                services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                }).
                AddJwtBearer(options =>
                {
                    options.Events = new JwtBearerEvents
                    {
                        OnAuthenticationFailed = context =>
                        {
                            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                            {
                                context.Response.Headers.Add("act", "expired");
                            }
                            return Task.CompletedTask;
                        }
                    };

                    options.SaveToken = true;
                    options.RequireHttpsMetadata = false;

                    options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
                    {
                        ValidateIssuerSigningKey = true,
                        ValidIssuer = jwtSettings.Issuer,
                        ValidAudience = jwtSettings.Audience,
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecurityKey)),
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        RequireExpirationTime = true,
                        ClockSkew = TimeSpan.Zero
                    };
                });
            }

            #endregion
        }


        #region 初始化全局静态配置

        /// <summary>
        /// 初始化全局静态配置
        /// </summary>
        private void InitConfiguration()
        {
            UldAdminGlobalContext.DBConfig = Configuration.GetSection("DBConfig").Get<DBConfig>();
            UldAdminGlobalContext.Configuration = Configuration;
            UldAdminGlobalContext.RedisConfig = Configuration.GetSection("RedisConfig").Get<RedisConfig>();
            UldAdminGlobalContext.JwtSettings = Configuration.GetSection("JwtSettings").Get<JwtSettings>();
            UldAdminGlobalContext.LogConfig = Configuration.GetSection("LogConfig").Get<LogConfig>();
            UldAdminGlobalContext.QuartzConfig = Configuration.GetSection("QuartzConfig").Get<QuartzConfig>();
            UldAdminGlobalContext.SystemCacheConfig = Configuration.GetSection("SystemCacheConfig").Get<SystemCacheConfig>();
            UldAdminGlobalContext.MongoDBConfig = Configuration.GetSection("MongoDBConfig").Get<MongoDBConfig>();
            UldAdminGlobalContext.SystemConfig = Configuration.GetSection("SystemConfig").Get<SystemConfig>();
            UldAdminGlobalContext.DirectoryConfig = Configuration.GetSection("DirectoryConfig").Get<DirectoryConfig>();
        }

        #endregion
    }
}
